"""
powcalib.py:

This script will be used to automate the process of performing the powder
calibration on the triple-axis.  It is general enough to handle any cubic powder.

The syntax of the command is:

powcalib.py powder_name count_time [energy energyvalue maxpeaks max_number_of peaks]

where 'energy energyvalue' and
      'maxpeaks max_number_of_peaks' are optional arguments

Example:

powcalib.py ni 5
(do a powder calibration for 5s/point using Nickel and the default energy)

powcalib.py si 1 energy 30.5 maxpeaks 7
(do a si powder calibration, 1s/pt, Ei=30.5 meV, with a maximum of 7 peaks)

Author: Mark Lumsden (lumsdenmd@ornl.gov)
Last modified: Thursday, October 2, 2003
"""

import sys, math
import PySpice

from instdefs import getpars, getprefs
from get_peaks import bccpeaks, fccpeaks, diamondpeaks, cmppeak_tth
from get_peaks import bccpeaks_l2, fccpeaks_l2, diamondpeaks_l2
from validpeak_new import validpeak_new
from genscans import genscans

pi = 3.14159265

# We need at least 2 arguments here, the powder name and the counting time

if len(sys.argv) < 3:
    raise ValueError, "The powder calibration script requires 1 or 2 arguments"

#Check to see if the powder given is a valid powder.
#The defined powders are specified in the instdefs.py file"

powders, powdictlist, scandir = getpars()
allowpopups = getprefs()

if sys.argv[1] in powders:
    powderindex=powders.index(sys.argv[1])
else:
    raise ValueError, "The specified powder name is not valid"

powdict=powdictlist[powderindex]

counttime=float(sys.argv[2])

# This next section parses the 2 optional arguments, energy and maxpeaks

if len(sys.argv) == 5:
    if sys.argv[3] == 'energy':
        energy_specified = 1
        maxpeaks_specified=0
        espec=float(sys.argv[4])
    elif sys.argv[3] == 'maxpeaks':
        energy_specified = 0
        maxpeaks_specified=1
        maxpeaks=int(float(sys.argv[4]))
    else:
        energy_specified = 0
        maxpeaks_specified=0
elif len(sys.argv) == 7:
    if sys.argv[3] == 'energy':
        if sys.argv[5] == 'maxpeaks':
            energy_specified = 1
            maxpeaks_specified=1
            espec=float(sys.argv[4])
            maxpeaks=int(float(sys.argv[6]))
        else:
            energy_specified = 1
            maxpeaks_specified=0
            espec=float(sys.argv[4])
    elif sys.argv[3] == 'maxpeaks':
        if sys.argv[5] == 'energy':
            energy_specified = 1
            maxpeaks_specified=1
            espec=float(sys.argv[6])
            maxpeaks=int((float(sys.argv[4])))
        else:
            energy_specified = 0
            maxpeaks_specified=1
            maxpeaks=int(float(sys.argv[4]))
    else:
        if sys.argv[5] == 'energy':
            energy_specified = 1
            maxpeaks_specified=0
            espec=float(sys.argv(6))
        elif sys.argv[5] == 'maxpeaks':
            energy_specified = 0
            maxpeaks_specified=1
            maxpeaks=int(float(sys.argv[6]))
        else:
            energy_specified = 0
            maxpeaks_specified=0
else:          
    energy_specified = 0
    maxpeaks_specified = 0

"""
Check Energy mode - if 0 or 1, assume that lambda/2 peaks are available
                  - if 2 or 3, assume no lambda/2
At the same time, we can set the energy value to be the current energy value
in the case of modes 2 and 3 (as these are permanently fixed energies) and to the
specified value in the case of modes 0 and 1 (if no value is specified, the
fixed_energy will be used).
"""

emode=PySpice.getvar('emode')
#emode=0
if (emode == 0) or (emode == 1):
    can_use_lambda_2=1
    if energy_specified == 1:
        # Use the specified energy
        energy = espec
    else:
        energy = PySpice.getvar('fixed_energy')
        #energy=14.7
elif emode == 2:
    energy=PySpice.getpos('ei')
    #energy=13.5
    can_use_lambda_2=0
elif emode == 3:
    energy=PySpice.getpos('ef')
    #energy=30.5
    can_use_lambda_2=0

if can_use_lambda_2:
    if energy <= 20:

        can_use_lambda_2 = 1

        if allowpopups:        
            string_to_print = 'The powder calibration script will use lambda/2 peaks \n Please ensure that any filters are removed from the beam'
            timeout = 240
            truebutton = 'Filters are removed'
            falsebutton = 'Cancel'
            title = 'Powder calibration warning'
            timeoutreturn = 0
            popupreturn=PySpice.twobuttontimed(string_to_print,timeout,timeoutreturn,truebutton,falsebutton,title)
            
            if popupreturn:
                pass
            else:
                print 'Error in the powder calibration script.\nThe powder calibration script was cancelled by the user.\n'
                sys.exit(0)
                
    else:
        can_use_lambda_2 = 0

# Calculate the wavelength, and look up the powder structure, lattice constant,
# and titletext from the dictionary information stored in instdefs.py

wavelength= math.sqrt(81.81/energy)

powder_structure=powdict['structure']
lattconst=powdict['lattconst']
titletext=powdict['titletext']

# From the powder structure, call the appropriate routine to generate the allowed
# reflections.

if powder_structure == 'fcc':
    peaklist=fccpeaks()
elif powder_structure == 'bcc':
    peaklist=bccpeaks()
elif powder_structure == 'diamond':
    peaklist=diamondpeaks()

# Peaklist now contains the list of peaks for this powder.
# We must now find the set of reflections which are allowed for Bragg's law and
# the constraints of the spectrometer

instrument_sign=PySpice.getvar('plusminus')
sample_sign=instrument_sign[1]

#sample_sign=-1

if sample_sign == 1:
    s2limit=PySpice.upperlimit('s2')
    #s2limit=130.2
elif sample_sign == -1:
    s2limit=PySpice.lowerlimit('s2')
    #s2limit=-120

newpeaklist=[]

collimation = PySpice.getvar('collimation')
dspace_mono = PySpice.getvar('dspace_mono')

thetam = math.asin(wavelength/(2*dspace_mono))
alpha1 = collimation[0]/60
alpha2 = collimation[1]/60
alpha3 = collimation[2]/60
beta = 0.5
term1 = (alpha1*alpha1*alpha2*alpha2)+(alpha1*alpha1*alpha3*alpha3)+(alpha2*alpha2*alpha3*alpha3)+4*beta*beta*((alpha2*alpha2)+(alpha3*alpha3))
term2 = 2*alpha2*alpha2*((alpha1*alpha1)+2*(beta*beta))/(math.tan(thetam))
term3 = 4*((alpha1*alpha1*alpha2*alpha2)+(alpha1*alpha1*beta*beta)+(alpha2*alpha2*beta*beta))/(math.tan(thetam)*math.tan(thetam))
const = (alpha1*alpha1)+(alpha2*alpha2)+(4*beta*beta)

for peak in peaklist:
    dspacing=math.sqrt(lattconst**2/(peak[0]**2+peak[1]**2+peak[2]**2)) 
    isvalid,tthmin,tthmax=validpeak_new(sample_sign,s2limit,wavelength,dspacing,term1,term2,term3,const)
    if isvalid:
        newpeaklist.append([peak,tthmin,tthmax])

"""
Now that we've got a list of lambda only peaks, we now need to consider whether we
should append the lambda/2 peaks as well.  As a rule, if you already have 5 or more,
don't bother.
"""

if can_use_lambda_2:
    if len(newpeaklist) < 5:
        if powder_structure == 'fcc':
            l2peaklist=fccpeaks_l2()
        elif powder_structure == 'bcc':
            l2peaklist=bccpeaks_l2()
        elif powder_structure == 'diamond':
            l2peaklist=diamondpeaks_l2()
        for peak in l2peaklist:
            dspacing=math.sqrt(lattconst**2/(peak[0]**2+peak[1]**2+peak[2]**2)) 
            isvalid,tthmin,tthmax=validpeak_new(sample_sign,s2limit,wavelength,dspacing,term1,term2,term3,const)
            if isvalid:
                newpeaklist.append([peak,tthmin,tthmax])

# This will sort the peaks based on the absolute value of 2theta

newpeaklist.sort(cmppeak_tth)

# Cut off the peaks at the specified number if it is given

if maxpeaks_specified == 1:
        # Use the specified energy
        newpeaklist=newpeaklist[:maxpeaks]

commlist=genscans(newpeaklist, scandir, titletext)

# Add some commands to the top of the list and append 'calibrate finish' to the
# bottom

if len(commlist) == 0:
    pass
else:
    commlist.insert(0,"drive ei %f"% energy)
    commlist.insert(0,"preset time %f"% counttime)
    commlist.insert(0,"calibrate begin standard %s energy %f"% (sys.argv[1], energy))
    commlist.append("calibrate finish")

#In this for loop, we will execute the commands needed for the calibration
    
for coms in commlist:
    PySpice.Command(coms)

#for coms in commlist:
#    print coms




